package com.jacky.webflux.jwt.token;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jacky.webflux.jwt.common.BasicResp;
import lombok.SneakyThrows;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;

@Component
public class TokenFilter implements WebFilter {

    private static final Logger LOGGER = LoggerFactory.getLogger(TokenFilter.class);

    private final JwtProperties jwtProperties;

    public TokenFilter(JwtProperties jwtProperties) {
        this.jwtProperties = jwtProperties;
    }

    @SneakyThrows
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();

        String reqPath = request.getPath().value();
        if (reqPath.contains("/auth/login") || reqPath.contains("/auth/registry")) {
            return chain.filter(exchange);
        }
        String authHeader = request.getHeaders().getFirst(HttpHeaders.AUTHORIZATION);
        if (StringUtils.isBlank(authHeader)) {
            LOGGER.error("authorization header is null");
            return this.responseWithErrorMessage(response, HttpStatus.NOT_ACCEPTABLE, "authorization header is null");
        }
        if (!authHeader.startsWith(jwtProperties.getTokenPrefix())) {
            LOGGER.error("unrecognized token prefix");
            return this.responseWithErrorMessage(response, HttpStatus.NOT_ACCEPTABLE, "unrecognized token prefix");
        }
        String tokenStr = authHeader.replace(jwtProperties.getTokenPrefix(), "");
        try {
            exchange.getAttributes().put("token", tokenStr);
        } catch (Exception e) {
            LOGGER.error("unrecognized token prefix");
            return this.responseWithErrorMessage(response, HttpStatus.INTERNAL_SERVER_ERROR, e.getMessage());
        }
        return chain.filter(exchange);
    }

    protected Mono<Void> responseWithErrorMessage(ServerHttpResponse response, HttpStatus status, String errMsg) throws JsonProcessingException {
        response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
        ObjectMapper om = new ObjectMapper();
        String respBody = om.writeValueAsString(BasicResp.fail(status.value(), errMsg));
        DataBuffer buffer = response.bufferFactory().wrap(respBody.getBytes(StandardCharsets.UTF_8));
        return response.writeWith(Mono.just(buffer));
    }
}
